﻿Imports System.Threading
Imports System.IO
Imports System.Dynamic

Public Enum ProcessingMode
    Decompressor
    Compressor
    Packer
End Enum

Public Enum Output
    File
    Folder
End Enum


Public Class PostValidationRules
    Private ReadOnly _target As ProcessingMode
    Private ReadOnly _percentageGain As Decimal
    Public ReadOnly Property Target As ProcessingMode
        Get
            Return _target
        End Get
    End Property
    Public ReadOnly Property PercentageGain As Decimal
        Get
            Return _percentageGain
        End Get
    End Property
    Sub New(ByVal target As ProcessingMode, Optional percentageGain As Decimal = 0)
        _target = target
        If percentageGain > 100 Or percentageGain < 0 Then _
            Throw New Exception("Gain must be between 0 - 100!")
        _percentageGain = percentageGain
    End Sub
End Class


Public Class FileSearcher
    Private ReadOnly _executable As String

    Public ReadOnly Property Executable As String
        Get
            Return _executable
        End Get
    End Property


    Sub New(executable As String, paths As String())
        For Each path In paths
            For Each file In My.Computer.FileSystem.GetFiles(path, FileIO.SearchOption.SearchAllSubDirectories)
                If My.Computer.FileSystem.GetName(file).ToLower() = executable.ToLower Then
                    _executable = file
                End If
            Next
        Next
        If _executable Is Nothing Then Throw New Exception("Cannot find """ & executable & """ in the given paths!")
    End Sub
End Class


Public Class Definition
    Public Property Mode As ProcessingMode
    Public Property ParameterTemplate As String
    Public Property AllowedTypes As New List(Of String)
    Public Property DeniedTypes As New List(Of String)
    Public Property Output As Output

    Public Sub Reset()
        Mode = Nothing
        ParameterTemplate = Nothing
        AllowedTypes.Clear()
        DeniedTypes.Clear()
        Output = Nothing
    End Sub
End Class


Public Class Processor
    Public Property Name As String
    Public Property Executable As FileSearcher
    Public Property Definitions As New List(Of Definition)
    Public Property MaximumThreads As Integer
    Public Property WindowStyle As ProcessWindowStyle
    Public Property OutputFormat As String
    Private Const OutputPrefix As String = "TD_"
    Private Const OutputSufix As String = "_KB"
    Public Property pause As Boolean
    Public Property PackingFolder As String

    Private Shared Function GetExtension(ByVal path As String) As String
        Dim extension As String = IO.Path.GetExtension(path)
        If extension = "" Then Return "EXTENSIONLESS"
        Return extension
    End Function

    Private Function IsProperDefinition(processingmode As ProcessingMode, selectedDefinition As Definition, inboundData As String)
        If selectedDefinition.Mode = processingmode And _
            (selectedDefinition.AllowedTypes.Count = 0 OrElse _
             selectedDefinition.AllowedTypes.IndexOf(GetExtension(inboundData)) >= 0) Then
            Return True
        End If
        Return False
    End Function

    Public Function GetTasks(processingMode As ProcessingMode, _
                             inboundData As String) As List(Of Task)
        Dim tasks As New List(Of Task)
        For Each selectedDefinition As Definition In Definitions
            If IsProperDefinition(ProcessingMode, selectedDefinition, inboundData) Then

                Dim partes() As String = Split(selectedDefinition.ParameterTemplate, "%")
                Dim outputFolder As String = _
                    My.Computer.FileSystem.GetParentPath(inboundData) & "\" & _
                    OutputPrefix & My.Computer.FileSystem.GetName(inboundData) & OutputSufix
                Dim parameter As String = _
                    GenerateParameter(selectedDefinition, inboundData, partes, outputFolder)
                Dim command As New ProcessStartInfo

                With command
                    .WindowStyle = WindowStyle
                    .FileName = "cmd.exe"
                    .Arguments = " /C """ & parameter
                End With

                tasks.Add(New Task(command, selectedDefinition.Output, outputFolder))

            End If
        Next
        Return tasks
    End Function

    Private Function GenerateParameter(ByVal selectedDefinition As Definition, _
                                       ByVal inboundData As String, _
                                       ByVal partes As String(), _
                                       ByRef outputFolder As String) As String

        Dim parameter As String = Nothing
        For Each parte As String In partes
            Select Case parte
                Case "in"
                    parameter += """" & inboundData & """"
                Case "out"
                    parameter += """"
                    If selectedDefinition.Output = Output.Folder Then
                        parameter += outputFolder
                        If Not Directory.Exists(outputFolder) Then
                            Directory.CreateDirectory(outputFolder)
                        End If

                    ElseIf selectedDefinition.Output = Output.File Then
                        parameter += inboundData
                        parameter += OutputFormat
                        outputFolder += OutputFormat
                    End If
                    parameter += """"
                Case "exec"
                    parameter += """" & Executable.Executable & """"
                Case Else
                    parameter = parameter & parte
            End Select
        Next
        parameter += """"
        If pause = True Then
            parameter += " & pause"
        End If
        Return parameter
    End Function
End Class


Public Class Task
    Public Property Commands As ProcessStartInfo
    Public Property Output As Output
    Public Property Path As String
    Sub New(ByVal commands As ProcessStartInfo, ByVal output As Output, ByVal path As String)
        Me.Commands = commands
        Me.Output = output
        Me.Path = path
    End Sub
End Class


Public Class KillerBytes
    Public Property Processors As New List(Of Processor)
    Public Property MaximumThreads As Integer
    Private _currentRunningThreads As Integer
    Private ReadOnly _queue As New List(Of Task)
    Private _thread As Thread
    Private Event TaskDone(ByRef s As String)
    Private _threadedTask As Task


    Public Sub Process(ByRef source As String) Handles Me.TaskDone
        Dim pack As Boolean = True

        If File.Exists(source) Then
            For Each processor As Processor In Processors
                For Each task As Task In processor.GetTasks(ProcessingMode.Decompressor, source)
                    If processor.PackingFolder Is Nothing Then processor.PackingFolder = _
                        My.Computer.FileSystem.GetParentPath(source)
                    _queue.Add(task)
                    pack = False
                Next
            Next
            If pack = True Then
                For Each processor As Processor In Processors
                    For Each task As Task In processor.GetTasks(ProcessingMode.Packer, source)
                        _queue.Add(task)
                    Next
                Next
            End If
            If _currentRunningThreads < MaximumThreads AndAlso _queue.Count > 0 Then
                _currentRunningThreads += 1
                _thread = New Thread(AddressOf Engine)
                _thread.Start()
            End If
        Else
            For Each file As String In My.Computer.FileSystem.GetFiles(source, FileIO.SearchOption.SearchAllSubDirectories)
                Process(file)
            Next
        End If
    End Sub


    Private Sub Engine()
        _threadedTask = _queue.First
        _queue.RemoveAt(0)
        MsgBox(_threadedTask.Commands.Arguments)
        Diagnostics.Process.Start(_threadedTask.Commands).WaitForExit()
        _currentRunningThreads -= 1
        RaiseEvent TaskDone(_threadedTask.Path)
    End Sub


    Sub New()
        MaximumThreads = 2
    End Sub


End Class